# -*- coding: utf8 -*-
#
# Copyright (c) 2009 Matías Ribecky <mail@mribecky.com.ar>
#
# This file is part of twitter-notifications.
# 
# twitter-notifications is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# twitter-notifications is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with twitter-notifications.  If not, see <http://www.gnu.org/licenses/>.

'''This module exports en interface to twitter HTTP api.

There are two models: :class:`User` and :class:`Status`. These shoudn't be
directly instanciated. For users, you can create them from an id or screen name
with the :meth:`~Api.get_user` method. Statuses can be created from an id with
the :meth:`~Api.get_status` method.

The :class:`Api` class exports the methods to interact with the server.

Here's an example of the basic usage::

    \'\'\'Get the user timeline and output it to stdout.\'\'\'

    api = Api('someuser', 'password')
    statuses = sorted(api.friends_timeline(), key=lambda s: s.date)
    for status in statuses:
        print status.user.name.encode('utf8')
        print '\t' + status.user.message.encode('utf8')

'''

import json
import urllib2
import datetime

import twitternotification.cache

VERIFY_ACCOUNT_URL = 'http://api.twitter.com/1/account/verify_credentials.json'
FRIENDS_TIMELINE_URL = 'http://api.twitter.com/1/statuses/' \
                                                        'friends_timeline.json'

class NetworkError(Exception):
    '''Thrown when there is a connection problem.'''

    pass

class ApiError(Exception):
    '''Throwed when the server returns an error.'''

    def __init__(self, request, msg):
        Exception.__init__(self, msg)
        self.request = request

class User(object):
    '''A user.

    .. attribute:: name
        :type: unicode

        The user real name.

    .. attribute:: user_name
        :type: unicode

        The user's screen name.

    .. attribute:: creation_date
        :type: datetime.datetime

        The moment the user was created.

    .. attribute:: image_url
        :type: unicode

        The url of the profile image.

    .. attribute:: image
        :type: unicode

        The local path to the cached profile image.

    .. attribute:: description
        :type: unicode

        The user's description.

    .. attribute:: url
        :type: unicode

        The user's profile url.

    '''
    def __init__(self, user_data):
        self.name = user_data['name']
        self.user_name = user_data['screen_name']
        self.creation_date = parse_date(user_data['created_at'])
        self.image_url = user_data['profile_image_url']
        self.image = twitternotification.cache.get_user_image(self.user_name,
                                                              self.image_url)
        self.description = user_data['description']
        self.url = user_data['url']

class Status(object):
    '''A status.

    .. attribute:: id
        :type: int

        The status' id.

    .. attribute:: date
        :type: datetime.datetime

        The moment when the status was published.

    .. attribute:: user
        :type: User

        The poster of the status.

    .. attribute:: message
        :type: unicode

        The content of the status.

    '''

    def __init__(self, status_data):
        self.id = status_data['id']
        self.date = parse_date(status_data['created_at'])
        self.message = status_data['text']
        self.user = User(status_data['user'])

class Api(object):
    '''Exposes the twitter api.

    .. attribute:: user
        :type: User

        The user with wich the requests authenticate.

    '''

    def __init__(self, username, password):
        self.user = None
        self._opener = None

        self._login(username, password)

    def _login(self, username, password):
        '''Creates the url opener with the authentication set up.'''

        auth_handler = urllib2.HTTPBasicAuthHandler()
        auth_handler.add_password(realm='Twitter API',
                                  uri='http://twitter.com',
                                  user=username,
                                  passwd=password
                                  )
        auth_handler.add_password(realm='Twitter API',
                                  uri='http://api.twitter.com',
                                  user=username,
                                  passwd=password
                                  )
        self._opener = urllib2.build_opener(auth_handler)

        user_data = self._call_api(VERIFY_ACCOUNT_URL)
        self.user = User(user_data)

    def _call_api(self, url):
        '''Request an url from the server and parse the output.

        :throws: NetworkError when connection to the server couldn't be
                    established.
        :trows: ApiError when the server returns an error.

        '''

        try:
            data_file = self._opener.open(url)
            data = json.load(data_file)
            assert 'error' not in data # not sure if this happens, report if
                                       # it does.
            return data
        except urllib2.HTTPError, error:
            error_data = json.load(error)
            raise ApiError(error_data['request'], error_data['error'])
        except urllib2.URLError:
            raise NetworkError("Could not connect to the server")


    def friends_timeline(self):
        '''Get a list of the friends' statuses.'''

        statuses = self._call_api(FRIENDS_TIMELINE_URL)

        for status in statuses:
            yield Status(status)

class DummyApi(object):
    def __init__(self, username=None, password=None):
        self.username = username
        self.password = password

    def friends_timeline(self):
        if self.username is None:
            raise ApiError(None, "Can't get the friends timeline on a non"
                                      " authenticated connection")

        avatar = 'http://s.twimg.com/a/1256083330/images/' \
                 'default_profile_%d_normal.png'

        users = [{'name': 'Test McTesty',
                  'screen_name': 'tmctesty',
                  'created_at': 'Sat Jan 20 22:00:00 +0000 2009',
                  'profile_image_url': avatar % 1,
                  'description': 'A test account',
                  'url': ''
                  },
                 {'name': 'Someone',
                  'screen_name': 'some',
                  'created_at': 'Sat Jan 22 22:00:00 +0000 2009',
                  'profile_image_url': avatar % 2,
                  'description': 'A test account',
                  'url': ''
                  }
                 ]
        statuses = [{'id': 1,
                     'created_at': 'Sat Jan 24 22:00:00 +0000 2009',
                     'text': 'Test tweet 1',
                     'user': users[0]
                     },
                    {'id': 3,
                     'created_at': 'Sat Jan 27 22:00:00 +0000 2009',
                     'text': 'Test tweet 3',
                     'user': users[0]
                     },
                    {'id': 2,
                     'created_at': 'Sat Jan 25 22:00:00 +0000 2009',
                     'text': 'Test tweet 2',
                     'user': users[1]
                     }
                   ]

        for status in statuses:
            yield Status(status)


_MONTHS = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
          'Oct', 'Nov', 'Dic']

def parse_date(date_string):
    try:
        day_name, month_name, day, time, offset, year = date_string.split()
    except ValueError:
        day_name, month_name, day, time, year = date_string.split()

    time_format = '%H:%M:%S'
    time = datetime.datetime.strptime(time, time_format)
    date = time.replace(year=int(year),
                        month=_MONTHS.index(month_name),
                        day=int(day)
                        )
    return date

